<?php

namespace Pinenut;

/**
 * Базовый класс приложения
 *
 * Реализует обработку запроса
 *
 * @author Kosh
 *        
 */
abstract class Application {
	
	use\Pinenut\Di\Injectable;
	
	/**
	 * Имена классов ядра, используемые в приложении,
	 * можно пронаследовать и подменить на свои
	 */
	/**
	 *
	 * @var \Pinenut\Request
	 */
	static $RequestClass = '\Pinenut\Request';
	
	/**
	 *
	 * @var \Pinenut\Response
	 */
	static $ResponseClass = '\Pinenut\Response';
	
	/**
	 *
	 * @var \Pinenut\Service\Router
	 */
	static $RouterServiceClass = '\Pinenut\Service\Router';
	
	/**
	 *
	 * @var \Pinenut\Service\Dispatcher
	 */
	static $DispatcherServiceClass = '\Pinenut\Service\Dispatcher';
	
	/**
	 *
	 * @var \Pinenut\Context
	 */
	static $ContextClass = '\Pinenut\Context';
	
	/**
	 *
	 * @var \Pinenut\Context\Auth
	 */
	static $ContextAuthClass = '\Pinenut\Context\Auth';
	
	/**
	 *
	 * @var \Pinenut\Log\File
	 */
	static $LogClass = '\Pinenut\Log\File';
	protected static $libraryPath;
	protected static $applicationPath;
	protected static $isDebug = false;
	protected static $environment;
	protected static $logFile;
	protected static $modules = [ ];
	
	/**
	 * Исполняется приложение
	 */
	static function run() {

		
		// подключаем модули
		static::prepareModules ();

		// настраивает зависимости
		new Di ( '\DiConfig' );
		
		// устанавливаем обработчик ошибок
		static::setErrorHandler ();
		
		try {
		// запускаем маршрутизацию, получаем запрос и исполняем действие с ним
		$request = static::route ();
		
		$context = static::context ( $request );
		
		$response = static::dispatch ( $context );
		
		echo $response->content;
		
		} catch (\Exception $e) {
			static::errorHandler(1, $e->getMessage(), $e->getFile(), $e->getLine(), $e->getCode(), $e->getTrace());
		}
	}
	
	/**
	 * Подключаем модули
	 *
	 * Настраиваем include paths
	 * Создаём массив путей для поиска вьюх
	 *
	 * TODO: отслеживание версий модулей, подключение зависимостей и т.д.
	 * TODO: список путей где могут быть модули и поиск в них модулей... но что-то как-то геморрно и долго каждый раз
	 */
	static function prepareModules() {
		$modulePath = static::$libraryPath . '/../modules/';
		$paths = [];
		foreach ( static::$modules as $module ) {
			array_unshift ( $paths, $modulePath . $module . '/library');
			array_unshift ( $paths, $modulePath . $module . '/application');
		}
		array_push ( $paths, static::$libraryPath );
		array_unshift ( $paths, static::$applicationPath );
		set_include_path ( implode ( PATH_SEPARATOR, $paths ) );
	}
	static protected function route() {
		$router = static::$RouterServiceClass;
		return $router::route ();
	}
	static protected function context(\Pinenut\Request $request) {
		return new static::$ContextClass ( $request );
	}
	static protected function dispatch($context) {
		$dispatcher = static::$DispatcherServiceClass;
		return $dispatcher::dispatch ( $context );
	}
	static function setErrorHandler() {
		set_error_handler ( array (
				'static',
				'errorHandler' 
		) );
		register_shutdown_function ( array (
				'\Application',
				'shutDownFunction' 
		) );
		ini_set ( 'display_errors', 0 );
	}
	static function shutDownFunction() {
		$error = error_get_last ();
		if (! ($error ['type'] & 8)) {
			static::errorHandler ( $error ['type'], $error ['message'], $error ['file'], $error ['line'] );
		}
	}
	static function errorHandler($errno, $errstr, $errfile, $errline, $code = 503, $trace = null) {
		if (! (error_reporting () & $errno)) {
			// This error code is not included in error_reporting
			return;
		}
		$error = array (
				'type' => $errno,
				'message' => $errstr,
				'file' => $errfile,
				'line' => $errline,
				'code' => $code
		);
		if (null === $trace) {
			$trace = debug_backtrace ();
		}
		// Логгируем ошибку
		
		
		// грохае буфер того, что вывели ранее
		ob_end_clean();
		
		// Выводим
		$request = new static::$RequestClass ( 'errorHandler', array (
				'error' => $error,
				'trace' => $trace 
		), null, false );
		
		$context = static::context ( $request );
		
		$response = static::dispatch ( $context );
		
		echo $response->content;
		
		return true;
		// exit;
	}
	static function libraryPath($path = null) {
		if (null !== $path)
			static::$libraryPath = $path;
		return static::$libraryPath;
	}
	static function applicationPath($path = null) {
		if (null !== $path)
			static::$applicationPath = $path;
		return static::$applicationPath;
	}
	static function isDebug($param = null) {
		if (null !== $param)
			static::$isDebug = $param;
		return static::$isDebug;
	}
	static function environment($str = null) {
		if (null !== $str)
			static::$environment = $str;
		return static::$environment;
	}
	static function logFile($name = null) {
		if (null !== $name)
			static::$logFile = $name;
		return static::$logFile;
	}
	
	
	/**
	 * Возвращаем объект аутентификации текущего контекста
	 * @return \Pinenut\Context\Auth
	 */
	static function auth() {
		$dispatcher = static::$DispatcherServiceClass;
		return $dispatcher::$currentContext->auth;
	}
}